package edu.uiowa.cs.coqview;

import java.awt.Color;
import java.awt.Paint;
import java.awt.BasicStroke;
import java.awt.Dimension;
import java.awt.BorderLayout;
import javax.swing.*;

import java.util.*;
import java.awt.event.*;

import edu.umd.cs.piccolo.*;
import edu.umd.cs.piccolo.activities.*;
import edu.umd.cs.piccolo.nodes.*;
import edu.umd.cs.piccolox.*;



public class KeyboardMenu extends PCanvas {
	private static final int KEY_WIDTH = 100;
	private static final int KEY_HEIGHT = 100;
	private static final int WITHIN_ROW_SPACE = 10;
	private static final int BETWEEN_ROW_SPACE = 10;
	private static final int CARD_WIDTH = 
		11*WITHIN_ROW_SPACE + 10*KEY_WIDTH + 2*KEY_WIDTH/3;
	private static final int CARD_HEIGHT = 4*BETWEEN_ROW_SPACE
	+ 3*KEY_HEIGHT;
	private static final int ROW2_NUDGE = 18;
	
	
	//The collection of base cards available
	//private Map<String, KMCard> cardForestRoots = new HashMap<String, KMCard>();
	
	//The name of the currently displayed base card
	private String currentBaseCard;
	
	//The set of all cards available
	private Map<String, KMCard> kmcCards = new HashMap<String, KMCard>();
	
	Stack<KMCard> kmcStack = new Stack<KeyboardMenu.KMCard>();
	
	List<KMKey> pressedKeyList = new ArrayList<KMKey>();
	Set<KMKey> vestigialPressedKeySet = new HashSet<KMKey>();
	
	//List of keys corresponding to the KMCards that are sub-cards of the
	//currentBaseCard
	List<KMKey> subCardList = new ArrayList<KMKey>();
	
	public KeyboardMenu() {
		setPreferredSize(new Dimension(
				11*WITHIN_ROW_SPACE
				+ 10*KEY_WIDTH
				+ 2*KEY_WIDTH/3
				+ 4*WITHIN_ROW_SPACE,
				4*BETWEEN_ROW_SPACE
				+ 3*KEY_HEIGHT
				+ 4*BETWEEN_ROW_SPACE));
		
		//turn off zooming and panning
		setPanEventHandler(null);
		setZoomEventHandler(null);		
	}
	
	//add a KMCard to the menu's stack
	public void pushKMCard(KMCard kmc) {
		kmcStack.push(kmc);
		getLayer().addChild(kmc);
	}
	
	public KMCard popKMCard() {
		KMCard removedChild = kmcStack.pop();
		getLayer().removeChild(removedChild);
		return removedChild;
	}
	
	public List<KMKey> getPressedKeyList() {
		return pressedKeyList;
	}
	
	//make a KMCard available/give it a name
	public void addKMCard(String name, KMCard kmc) {
		kmcCards.put(name, kmc);
	}
	
	public KMCard getKMCard(String name) {
		return kmcCards.get(name);
	}
	
	public class KMCard extends PLayer {
		//a list containing the portions of the KMCard that are
		//neither keys nor filled-in holes (i.e. the areas around keys,
		//excluding the places where keys appear to be missing)
		private List<PPath> nonKeysList = new ArrayList<PPath>();
		
		//the missing key positions, with their corresponding characters
		private Map<KMKey,PPath> filledHoles = new HashMap<KMKey,PPath>();
		
		//the (non-missing) keys, with corresponding characters
		Map<KMKey,PPath> keys = new HashMap<KMKey,PPath>();
		
		//the hole outlines
		private Map<KMKey,PPath> outlines = new HashMap<KMKey,PPath>();
		
		//the text to be placed on the keys
		private Map<KMKey, String> keyTextMap = new HashMap<KMKey,String>();
		
		//the images to be placed on the keys
		private Map<KMKey, PImage> keyIconMap = new HashMap<KMKey,PImage>();
		
		//the subCards (sub-menus) with their associated keys
		private Map<KMKey, KMCard> subCardMap = new HashMap<KMKey, KMCard>();
		
		private Map<KMKey, AbstractAction> keyActionMap = new HashMap<KMKey, AbstractAction>();
		
		Paint backPaint = new Color(120, 120, 255);
		Paint keyPaint = new Color(200, 200, 255);
		
		//coordinates of top-left corner
		float x, y;
		
		KMCard(float x, float y) {
			this.x = x;
			this.y = y;
			//createCardBack(x, y, backPaint);
			createCardBack();
			fillAllHoles();
		}
		
		public KMCard getSubCard(KMKey kmk) {
			return subCardMap.get(kmk);
		}
		
		public void setSubCard(KMKey kmk, KMCard kmc) {
			subCardMap.put(kmk, kmc);
		}
		
		public void setKeyAction(KMKey kmk, AbstractAction action) {
			keyActionMap.put(kmk, action);
		}
		
		public AbstractAction getKeyAction(KMKey kmk) {
			return keyActionMap.get(kmk);
		}
		
		public void performKeyAction(KMKey kmk) {
			keyActionMap.get(kmk).actionPerformed(null);
		}
		
		public void performKeyAction(KMKey kmk, ActionEvent e) {
			keyActionMap.get(kmk).actionPerformed(e);
		}
		
		//private void createCardBack(float x, float y, Paint backPaint) {
		private void createCardBack() {
			PPath section;  //non-key section of the back
			
			//top bar
			section = PPath.createRectangle(x, y, 
					CARD_WIDTH, BETWEEN_ROW_SPACE);
			section.setPaint(backPaint);	
			section.setStrokePaint(backPaint);
			addChild(section);
			nonKeysList.add(section);
			//first row vertical bars
			for (int i = 0; i < 10; i++) {
				section = PPath.createRectangle(
						x+i*(KEY_WIDTH+WITHIN_ROW_SPACE),
						y+BETWEEN_ROW_SPACE, WITHIN_ROW_SPACE, KEY_HEIGHT);
				section.setPaint(backPaint);
				section.setStrokePaint(backPaint);
				addChild(section);
				nonKeysList.add(section);
			}
			//end of row
			section= PPath.createRectangle(
					x+10*(KEY_WIDTH+WITHIN_ROW_SPACE),
					y+BETWEEN_ROW_SPACE, WITHIN_ROW_SPACE+(2*KEY_WIDTH)/3,
					KEY_HEIGHT);
			section.setPaint(backPaint);
			section.setStrokePaint(backPaint);
			addChild(section);
			nonKeysList.add(section);
			//second horizontal bar
			section = PPath.createRectangle(x, y+BETWEEN_ROW_SPACE+KEY_HEIGHT,
					CARD_WIDTH, BETWEEN_ROW_SPACE);
			section.setPaint(backPaint);
			section.setStrokePaint(backPaint);
			addChild(section);
			nonKeysList.add(section);
			//start of second row
			section = PPath.createRectangle(
					x,y+2*BETWEEN_ROW_SPACE+KEY_HEIGHT,
					WITHIN_ROW_SPACE+KEY_WIDTH/3 - ROW2_NUDGE,
					KEY_HEIGHT);
			section.setPaint(backPaint);
			section.setStrokePaint(backPaint);
			addChild(section);
			nonKeysList.add(section);
			//second row inner bars
			for (int i = 0; i < 9; i++) {
				section = PPath.createRectangle(
						x + i*(KEY_WIDTH+WITHIN_ROW_SPACE)
						+ (WITHIN_ROW_SPACE+KEY_WIDTH/3 + 
								KEY_WIDTH - ROW2_NUDGE),
						y+2*BETWEEN_ROW_SPACE+KEY_HEIGHT,
						WITHIN_ROW_SPACE, KEY_HEIGHT);
				section.setPaint(backPaint);
				section.setStrokePaint(backPaint);
				addChild(section);
				nonKeysList.add(section);
			}
			//end of second row
			section = PPath.createRectangle(
					x + 9*(KEY_WIDTH+WITHIN_ROW_SPACE)
					+ (WITHIN_ROW_SPACE+KEY_WIDTH/3 + KEY_WIDTH - ROW2_NUDGE),
					y+2*BETWEEN_ROW_SPACE+KEY_HEIGHT,
					WITHIN_ROW_SPACE + KEY_WIDTH/3 + ROW2_NUDGE, KEY_HEIGHT);
			section.setPaint(backPaint);
			section.setStrokePaint(backPaint);
			addChild(section);
			nonKeysList.add(section);
			//third horizontal bar
			section = PPath.createRectangle(x, y+2*BETWEEN_ROW_SPACE+2*KEY_HEIGHT,
					CARD_WIDTH, BETWEEN_ROW_SPACE);
			section.setPaint(backPaint);
			section.setStrokePaint(backPaint);
			addChild(section);
			nonKeysList.add(section);
			//start of third row
			section = PPath.createRectangle(
					x, y+3*BETWEEN_ROW_SPACE+2*KEY_HEIGHT,
					(2*KEY_WIDTH)/3 + WITHIN_ROW_SPACE, KEY_HEIGHT);
			section.setPaint(backPaint);
			section.setStrokePaint(backPaint);
			addChild(section);
			nonKeysList.add(section);
			//third row vertical bars
			for (int i = 0; i < 10; i++) {
				section = PPath.createRectangle(
						x + (2*KEY_WIDTH)/3  + WITHIN_ROW_SPACE
						+ KEY_WIDTH + i*(KEY_WIDTH+WITHIN_ROW_SPACE),
						y+3*BETWEEN_ROW_SPACE+2*KEY_HEIGHT,
						WITHIN_ROW_SPACE, KEY_HEIGHT);
				section.setPaint(backPaint);
				section.setStrokePaint(backPaint);
				addChild(section);
				nonKeysList.add(section);
			}
			//fourth horizontal bar
			section = PPath.createRectangle(x, y+3*BETWEEN_ROW_SPACE+3*KEY_HEIGHT,
					CARD_WIDTH, BETWEEN_ROW_SPACE);
			section.setPaint(backPaint);
			section.setStrokePaint(backPaint);
			addChild(section);
			nonKeysList.add(section);
		}
		
		private float[] getKeyOffset(KMKey kmk) {
			float deltax = 0;
			float deltay = 0;
			switch (kmk) {
				case Q:
					deltax = WITHIN_ROW_SPACE;
					deltay = BETWEEN_ROW_SPACE;
					break;
				case W:
					deltax = 2*WITHIN_ROW_SPACE+KEY_WIDTH;
					deltay = BETWEEN_ROW_SPACE;
					break;
				case E:
					deltax = 3*WITHIN_ROW_SPACE+2*KEY_WIDTH;
					deltay = BETWEEN_ROW_SPACE;
					break;
				case R:
					deltax = 4*WITHIN_ROW_SPACE+3*KEY_WIDTH;
					deltay = BETWEEN_ROW_SPACE;
					break;
				case T:
					deltax = 5*WITHIN_ROW_SPACE+4*KEY_WIDTH;
					deltay = BETWEEN_ROW_SPACE;
					break;
				case Y:
					deltax = 6*WITHIN_ROW_SPACE+5*KEY_WIDTH;
					deltay = BETWEEN_ROW_SPACE;
					break;
				case U:
					deltax = 7*WITHIN_ROW_SPACE+6*KEY_WIDTH;
					deltay = BETWEEN_ROW_SPACE;
					break;
				case I:
					deltax = 8*WITHIN_ROW_SPACE+7*KEY_WIDTH;
					deltay = BETWEEN_ROW_SPACE;
					break;
				case O:
					deltax = 9*WITHIN_ROW_SPACE+8*KEY_WIDTH;
					deltay = BETWEEN_ROW_SPACE;
					break;
				case P:
					deltax = 10*WITHIN_ROW_SPACE+9*KEY_WIDTH;
					deltay = BETWEEN_ROW_SPACE;
					break;
				case A:
					deltax = WITHIN_ROW_SPACE+KEY_WIDTH/3 - ROW2_NUDGE;
					deltay = 2*BETWEEN_ROW_SPACE+KEY_HEIGHT;
					break;
				case S:
					deltax = 2*WITHIN_ROW_SPACE+KEY_WIDTH
					+KEY_WIDTH/3 - ROW2_NUDGE;
					deltay = 2*BETWEEN_ROW_SPACE+KEY_HEIGHT;
					break;
				case D:
					deltax = 3*WITHIN_ROW_SPACE+2*KEY_WIDTH
					+KEY_WIDTH/3 - ROW2_NUDGE;
					deltay = 2*BETWEEN_ROW_SPACE+KEY_HEIGHT;
					break;
				case F:
					deltax = 4*WITHIN_ROW_SPACE+3*KEY_WIDTH
					+KEY_WIDTH/3 - ROW2_NUDGE;
					deltay = 2*BETWEEN_ROW_SPACE+KEY_HEIGHT;
					break;
				case G:
					deltax = 5*WITHIN_ROW_SPACE+4*KEY_WIDTH
					+KEY_WIDTH/3 - ROW2_NUDGE;
					deltay = 2*BETWEEN_ROW_SPACE+KEY_HEIGHT;
					break;
				case H:
					deltax = 6*WITHIN_ROW_SPACE+5*KEY_WIDTH
					+KEY_WIDTH/3 - ROW2_NUDGE;
					deltay = 2*BETWEEN_ROW_SPACE+KEY_HEIGHT;
					break;
				case J:
					deltax = 7*WITHIN_ROW_SPACE+6*KEY_WIDTH
					+KEY_WIDTH/3 - ROW2_NUDGE;
					deltay = 2*BETWEEN_ROW_SPACE+KEY_HEIGHT;
					break;
				case K:
					deltax = 8*WITHIN_ROW_SPACE+7*KEY_WIDTH
					+KEY_WIDTH/3 - ROW2_NUDGE;
					deltay = 2*BETWEEN_ROW_SPACE+KEY_HEIGHT;
					break;
				case L:
					deltax = 9*WITHIN_ROW_SPACE+8*KEY_WIDTH
					+KEY_WIDTH/3 - ROW2_NUDGE;
					deltay = 2*BETWEEN_ROW_SPACE+KEY_HEIGHT;
					break;
				case SEMICOLON:
					deltax = 10*WITHIN_ROW_SPACE+9*KEY_WIDTH
					+KEY_WIDTH/3 - ROW2_NUDGE;
					deltay = 2*BETWEEN_ROW_SPACE+KEY_HEIGHT;
					break;
				case Z:
					deltax = (2*KEY_WIDTH)/3 + WITHIN_ROW_SPACE;
					deltay = 3*BETWEEN_ROW_SPACE+2*KEY_HEIGHT;
					break;
				case X:
					deltax = (2*KEY_WIDTH)/3 + 2*WITHIN_ROW_SPACE+KEY_WIDTH;
					deltay = 3*BETWEEN_ROW_SPACE+2*KEY_HEIGHT;
					break;
				case C:
					deltax = (2*KEY_WIDTH)/3 + 3*WITHIN_ROW_SPACE+2*KEY_WIDTH;
					deltay = 3*BETWEEN_ROW_SPACE+2*KEY_HEIGHT;
					break;
				case V:
					deltax = (2*KEY_WIDTH)/3 + 4*WITHIN_ROW_SPACE+3*KEY_WIDTH;
					deltay = 3*BETWEEN_ROW_SPACE+2*KEY_HEIGHT;
					break;
				case B:
					deltax = (2*KEY_WIDTH)/3 + 5*WITHIN_ROW_SPACE+4*KEY_WIDTH;
					deltay = 3*BETWEEN_ROW_SPACE+2*KEY_HEIGHT;
					break;
				case N:
					deltax = (2*KEY_WIDTH)/3 + 6*WITHIN_ROW_SPACE+5*KEY_WIDTH;
					deltay = 3*BETWEEN_ROW_SPACE+2*KEY_HEIGHT;
					break;
				case M:
					deltax = (2*KEY_WIDTH)/3 + 7*WITHIN_ROW_SPACE+6*KEY_WIDTH;
					deltay = 3*BETWEEN_ROW_SPACE+2*KEY_HEIGHT;
					break;
				case COMMA:
					deltax = (2*KEY_WIDTH)/3 + 8*WITHIN_ROW_SPACE+7*KEY_WIDTH;
					deltay = 3*BETWEEN_ROW_SPACE+2*KEY_HEIGHT;
					break;
				case PERIOD:
					deltax = (2*KEY_WIDTH)/3 + 9*WITHIN_ROW_SPACE+8*KEY_WIDTH;
					deltay = 3*BETWEEN_ROW_SPACE+2*KEY_HEIGHT;
					break;
				case SLASH:
					deltax = (2*KEY_WIDTH)/3 + 10*WITHIN_ROW_SPACE+9*KEY_WIDTH;
					deltay = 3*BETWEEN_ROW_SPACE+2*KEY_HEIGHT;
					break;
			}
			float[] offset = {deltax, deltay};
			return offset;
		}

		private void fillAllHoles() {
			for (KMKey key : KeyboardMenu.KMKey.values()) {
				//if key is not in the set of (non-missing) keys
				//if (!keys.containsKey(key))
					fillHole(key);
			}
		}
		
		public void cutHole(KMKey key) {
			if (keys.containsKey(key)) {
				//remove the key
				keys.get(key).removeFromParent();
				keys.remove(key);
			}
			if (filledHoles.containsKey(key)) {
				//remove the fill
				filledHoles.get(key).removeFromParent();
				filledHoles.remove(key);
			}
			//add outline
			outlineHole(key);
		}
		
		public void removeFill(KMKey key) {
			if (filledHoles.containsKey(key)) {
				filledHoles.get(key).removeFromParent();
				filledHoles.remove(key);
			}
		}
		
		public void removeOutline(KMKey key) {
			if (outlines.containsKey(key)) {
				outlines.get(key).removeFromParent();
				outlines.remove(key);
			}
		}
		
		public void addKey(KMKey toAdd, PImage img,
				String text, Paint keyPaint) {
			//TO DO:  add support for images
			PPath key;
			PText commandLabel = new PText(text);
			commandLabel.setHorizontalAlignment(CENTER_ALIGNMENT);
			PText keyLabel = new PText(
					new String(new char[] {toAdd.getKeyChar()}));
			
			float [] offsets = getKeyOffset(toAdd);
			float deltax = offsets[0];
			float deltay = offsets[1];
			

			
			key = PPath.createRectangle(x+deltax, y+deltay, 
					KEY_WIDTH, KEY_HEIGHT);
			key.setPaint(keyPaint);
			
			commandLabel.setOffset(x+deltax+
					KEY_WIDTH/2-commandLabel.getWidth()/2, 
					y+deltay+KEY_HEIGHT/2-commandLabel.getHeight()/2);
		
			keyLabel.setOffset(x+deltax+KEY_WIDTH-keyLabel.getWidth()-3,
					y+deltay+KEY_HEIGHT-keyLabel.getHeight()-3);
			
			keys.put(toAdd,key);
			keyTextMap.put(toAdd, text);
			keyIconMap.put(toAdd, img);
			
			addChild(key);
			addChild(commandLabel);
			addChild(keyLabel);
		}

		public void addKey(KMKey toAdd, String text) {
			addKey(toAdd, null, text, keyPaint);
		}
		
		public void removeKey(KMKey toRemove) {
			//TO DO
		}
		
		public PPath getKey(KMKey k) {
			return keys.get(k);
		}
		
		public void fillHole(KMKey toFill) {
			PPath holeFill;
			
			float [] offsets = getKeyOffset(toFill);
			float deltax = offsets[0];
			float deltay = offsets[1];
					

			holeFill = PPath.createRectangle(x+deltax, y+deltay, 
					KEY_WIDTH, KEY_HEIGHT);
			holeFill.setPaint(backPaint);
			holeFill.setStrokePaint(new Color(0,0,0));
			
			filledHoles.put(toFill, holeFill);
			
			addChild(holeFill);
			
		}
		
		public void outlineHole(KMKey toOutline) {
			PPath outline;
			
			float [] offsets = getKeyOffset(toOutline);
			float deltax = offsets[0];
			float deltay = offsets[1];
			
			outline = PPath.createRectangle(x+deltax, y+deltay, 
					KEY_WIDTH, KEY_HEIGHT);
			outline.setPaint(new Color(0,0,0,0));
			outline.setStrokePaint(new Color(0,0,0));
			
			outlines.put(toOutline, outline);
			
			addChild(outline);
		}
		
		public void setBackPaint(Paint newPaint) {
			for (PPath rect : nonKeysList) {
				rect.setPaint(newPaint);
				rect.setStrokePaint(newPaint);
			}
			for (PPath filledHole : filledHoles.values()) {
				filledHole.setPaint(newPaint);
				//filledHole.setStrokePaint(newPaint);
			}
			backPaint = newPaint;
		}
		
		public void setKeyPaint(Paint newPaint) {
			keyPaint = newPaint;
			for (PPath key : keys.values()) {
				key.setPaint(newPaint);
				key.setStrokePaint(newPaint);
			}
		}
		
	}
	
	public enum KMKey {
		Q ('Q'),
		W ('W'),
		E ('E'), 
		R ('R'),
		T ('T'),
		Y ('Y'),
		U ('U'),
		I ('I'),
		O ('O'),
		P ('P'),
		A ('A'),
		S ('S'),
		D ('D'),
		F ('F'),
		G ('G'),
		H ('H'),
		J ('J'),
		K ('K'),
		L ('L'),
		SEMICOLON (';'),
		Z ('Z'),
		X ('X'),
		C ('C'),
		V ('V'),
		B ('B'),
		N ('N'),
		M ('M'),
		COMMA (','),
		PERIOD ('.'),
		SLASH ('/');
		
		private final char keyChar;
		KMKey(char str) {
			this.keyChar = str;
		}
		
		char getKeyChar() { return keyChar; }
	}
	
	public void setKeyBindings(JPanel jpnl) {
		KMKey [] kmKeys = {KMKey.Q,KMKey.W,KMKey.E,KMKey.R,KMKey.T,
				KMKey.Y,KMKey.U,KMKey.I,KMKey.O,KMKey.P,KMKey.A,
				KMKey.S,KMKey.D,KMKey.F,KMKey.G,KMKey.H,KMKey.J,
				KMKey.K,KMKey.L,KMKey.SEMICOLON,KMKey.Z,KMKey.X,
				KMKey.C,KMKey.V,KMKey.B,KMKey.N,KMKey.M,KMKey.COMMA,
				KMKey.PERIOD,KMKey.SLASH
		};
		
		String [] keyNames = { "q","w","e","r","t","y","u","i","o","p",
				"a","s","d","f","g","h","j","k","l","semicolon","z","x","c",
				"v","b","n","m","comma","period","slash"}; 
		
		Action qPressed = null, wPressed = null, ePressed = null,
		rPressed = null, tPressed = null, yPressed = null, 
		uPressed = null, iPressed = null, oPressed = null,
		pPressed = null, aPressed = null, sPressed = null,
		dPressed = null, fPressed = null, gPressed = null,
		hPressed = null, jPressed = null, kPressed = null,
		lPressed = null, semicolonPressed = null, zPressed= null,
		xPressed = null, cPressed = null, vPressed = null,
		bPressed= null, nPressed = null, mPressed = null,
		commaPressed = null, periodPressed = null, slashPressed = null;
		
		Action [] pressedActions = {
				qPressed , wPressed, ePressed,
				rPressed, tPressed, yPressed, 
				uPressed, iPressed, oPressed,
				pPressed, aPressed, sPressed,
				dPressed, fPressed, gPressed,
				hPressed, jPressed, kPressed,
				lPressed, semicolonPressed, zPressed,
				xPressed, cPressed, vPressed,
				bPressed, nPressed, mPressed,
				commaPressed, periodPressed, slashPressed};
		
		int i = 0;
		class KeyPressedAction extends AbstractAction {
			KMKey kmk;
			
			KeyPressedAction(KMKey kmk) {
				this.kmk = kmk;
			}
			
			public void actionPerformed(ActionEvent e) {
				
				if ( !(pressedKeyList.contains(kmk) | vestigialPressedKeySet.contains(kmk)) ) { 
					pressedKeyList.add(kmk);
					System.out.println(kmk.toString() +  " was pressed"); //for testing
					System.out.println("pressedKeyList: " + pressedKeyList.toString()); //for testing
					
					if (kmcStack.peek().getSubCard(kmk) != null) {
						//Make sure the outlines on the current (soon-to-be old) top card are normal
						for (PPath key : kmcStack.peek().keys.values()) {
							key.setStroke(new BasicStroke((float) 1.0));
						}
						
						pushKMCard(kmcStack.peek().getSubCard(kmk));
						subCardList.add(kmk);
					}
					else {
						System.out.println("no subcard, apparently");  //for testing
						if (kmcStack.peek().getKey(kmk) != null) {
							kmcStack.peek().getKey(kmk).setStroke(new BasicStroke((float) 3.0));
							
							//	check if there is an action to perform and, if so, perform it
							if (kmcStack.peek().getKeyAction(kmk) != null) {
								kmcStack.peek().performKeyAction(kmk, e);
							} else {
								System.out.println("No action to perform");
							}
						}
					}
				}
			}
		}
		for (Action pressedAction : pressedActions) {
			pressedAction = new KeyPressedAction(kmKeys[i]);
			jpnl.getInputMap().put(
					KeyStroke.getKeyStroke(kmKeys[i].toString()), 
					kmKeys[i].toString()+"Pressed");
			jpnl.getActionMap().put(kmKeys[i].toString()+"Pressed", pressedAction);
			i++;
		}
		
		
		Action qReleased = null, wReleased = null, eReleased = null,
		rReleased = null, tReleased = null, yReleased = null, 
		uReleased = null, iReleased = null, oReleased = null,
		pReleased = null, aReleased = null, sReleased = null,
		dReleased = null, fReleased = null, gReleased = null,
		hReleased = null, jReleased = null, kReleased = null,
		lReleased = null, semicolonReleased = null, zReleased = null,
		xReleased = null, cReleased = null, vReleased = null,
		bReleased = null, nReleased = null, mReleased = null,
		commaReleased = null, periodReleased = null, slashReleased = null;
		
		Action [] releasedActions = {
				qReleased, wReleased, eReleased,
				rReleased, tReleased, yReleased, 
				uReleased, iReleased, oReleased,
				pReleased, aReleased, sReleased,
				dReleased, fReleased, gReleased,
				hReleased, jReleased, kReleased,
				lReleased, semicolonReleased, zReleased,
				xReleased, cReleased, vReleased,
				bReleased, nReleased, mReleased,
				commaReleased, periodReleased, slashReleased};

		
		i = 0;
		class KeyReleasedAction extends AbstractAction {
			KMKey kmk;
			
			KeyReleasedAction(KMKey kmk) {
				this.kmk = kmk;
			}
			
			public void actionPerformed(ActionEvent e) {
				System.out.println(kmk.toString() + " was released"); //for testing
				if (vestigialPressedKeySet.contains(kmk)) {
					vestigialPressedKeySet.remove(kmk);
				}
				if (pressedKeyList.contains(kmk)) {
					int index = pressedKeyList.indexOf(kmk);
					vestigialPressedKeySet.addAll(pressedKeyList.subList(index+1, pressedKeyList.size()));
					System.out.println("vestigialPressedKeySet: " + vestigialPressedKeySet.toString()); //for testing
					pressedKeyList = pressedKeyList.subList(0, index);
					System.out.println("pressedKeyList: " + pressedKeyList.toString()); //for testing
				}
				if (subCardList.contains(kmk)) {
					int cardsToPop = subCardList.size() - subCardList.indexOf(kmk);
					for (int i = 0; i < cardsToPop; i++ ) {
						for (PPath key : kmcStack.peek().keys.values()) {
							key.setStroke(new BasicStroke((float) 1.0));
						}
						popKMCard();
						subCardList.remove(subCardList.size()-1);
					}
				}
				for (PPath key : kmcStack.peek().keys.values()) {
					key.setStroke(new BasicStroke((float) 1.0));
				}
			}
		}
		for (Action releasedAction : releasedActions) {
			releasedAction = new KeyReleasedAction(kmKeys[i]);
			jpnl.getInputMap().put(
					KeyStroke.getKeyStroke("released " + kmKeys[i].toString()), 
					kmKeys[i].toString()+"Released");
			jpnl.getActionMap().put(kmKeys[i].toString()+"Released", releasedAction);
			i++;
		}
		
	}
	
	
	
	public static void main(String[] args) {
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				JFrame jfrm = new JFrame();
				jfrm.setTitle("Keyboard Menu Test");
				jfrm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
				
				KeyboardMenu kbm = new KeyboardMenu();
				
				Color lightBlue = new Color(120, 120, 255);
				Color lighterBlue = new Color(200, 200, 255);
				
				KeyboardMenu.KMCard temp = kbm.new KMCard(10, 10);
				temp.setBackPaint(lightBlue);
				
				temp.fillHole(KMKey.Q);
				temp.fillHole(KMKey.W);
				temp.fillHole(KMKey.E);
				temp.addKey(KMKey.R,null, "more tactics\n==>",lighterBlue);
				temp.fillHole(KMKey.T);
				temp.fillHole(KMKey.Y);
				temp.fillHole(KMKey.U);
				temp.addKey(KMKey.I, null, "up", lighterBlue);
				temp.fillHole(KMKey.O);
				temp.fillHole(KMKey.P);
				temp.addKey(KMKey.A, null, "intros", lighterBlue);
				temp.addKey(KMKey.S, null, "assumption", lighterBlue);
				temp.addKey(KMKey.D, null, "rewrite", lighterBlue);
				temp.addKey(KMKey.F, null, "apply\n==>", lighterBlue);
				temp.fillHole(KMKey.G);
				temp.fillHole(KMKey.H);
				temp.addKey(KMKey.J, null, "left", lighterBlue);
				temp.addKey(KMKey.K, null, "down", lighterBlue);
				temp.addKey(KMKey.L, null, "right", lighterBlue);
				temp.fillHole(KMKey.SEMICOLON);
				temp.fillHole(KMKey.Z);
				temp.fillHole(KMKey.X);
				temp.fillHole(KMKey.C);
				temp.addKey(KMKey.V, null, "find similar\n==>", lighterBlue);
				temp.fillHole(KMKey.B);
				temp.fillHole(KMKey.N);
				temp.fillHole(KMKey.M);
				temp.fillHole(KMKey.COMMA);
				temp.fillHole(KMKey.PERIOD);
				temp.fillHole(KMKey.SLASH);
				
				kbm.addKMCard("base", temp);
				kbm.pushKMCard(temp);
				
				Color lightGreen = new Color(150, 255, 150);
				Color lighterGreen = new Color(200,255,200);
				
				temp = kbm.new KMCard(20,20);
				temp.setBackPaint(lightGreen);
				
				temp.fillHole(KMKey.Q);
				temp.fillHole(KMKey.W);
				temp.fillHole(KMKey.E);
				//leave R as a hole
				//temp.outlineHole(KMKey.R);
				temp.cutHole(KMKey.R);
				temp.fillHole(KMKey.T);
				temp.fillHole(KMKey.Y);
				temp.fillHole(KMKey.U);
				temp.fillHole(KMKey.I);
				temp.fillHole(KMKey.O);
				temp.fillHole(KMKey.P);
				temp.fillHole(KMKey.A);
				temp.fillHole(KMKey.S);
				temp.fillHole(KMKey.D);
				temp.fillHole(KMKey.F);
				temp.fillHole(KMKey.G);
				temp.fillHole(KMKey.H);
				temp.addKey(KMKey.J, null, "cases", lighterGreen);
				temp.addKey(KMKey.K, null, "simplify", lighterGreen);
				temp.addKey(KMKey.L, null, "reflexivity", lighterGreen);
				temp.addKey(KMKey.SEMICOLON, null, "cut", lighterGreen);
				temp.fillHole(KMKey.Z);
				temp.fillHole(KMKey.X);
				temp.fillHole(KMKey.C);
				temp.fillHole(KMKey.V);
				temp.fillHole(KMKey.B);
				temp.fillHole(KMKey.N);
				temp.fillHole(KMKey.M);
				temp.fillHole(KMKey.COMMA);
				temp.fillHole(KMKey.PERIOD);
				temp.fillHole(KMKey.SLASH);
				
				kbm.addKMCard("base_r", temp);
				
				kbm.kmcCards.get("base").setSubCard(KMKey.R, temp);
				
				JTextArea textArea = new JTextArea(5,20);
				textArea.setEditable(false);
				JScrollPane scrollPane = new JScrollPane(textArea);
				
				
				
				
				
				//jfrm.setLayout(new BorderLayout());
				
				//these break the thing
				//jfrm.getContentPane().add(scrollPane, BorderLayout.NORTH);
//				jfrm.add(scrollPane, BorderLayout.NORTH);
	//			jfrm.add(textArea, BorderLayout.NORTH);
				
				//this actually works
				//jfrm.add(new JLabel("fdsa"), BorderLayout.NORTH);
				
				//this doesn't work for some reason
				//jfrm.getContentPane().addKeyListener(new TestKBMenuKeyListener(kbm));
				
				//jfrm.addKeyListener(new TestKBMenuKeyListener(kbm));
			
				
				
				//jfrm.getContentPane().add(kbm, BorderLayout.SOUTH);
				
				JPanel jpanel = new JPanel();
				
				kbm.setKeyBindings(jpanel);
				
				jpanel.getInputMap();
				
				jpanel.setLayout(new BorderLayout());
				jpanel.add(kbm, BorderLayout.SOUTH);
				jpanel.add(scrollPane,BorderLayout.NORTH);
				jfrm.add(jpanel);
				
				Action spaceAction = new AbstractAction() {
					public void actionPerformed(ActionEvent e) {
						System.out.println("Spacebar was pressed");
					}
				};
				
				jpanel.
				getInputMap()
				.put(KeyStroke.getKeyStroke("SPACE"), "spacePressed");
				jpanel.
				getActionMap().put("spacePressed", spaceAction);
				
				
				KBMenuRPressedAction kbmRPressedAction = 
					new KBMenuRPressedAction(kbm);
				KBMenuRReleasedAction kbmRReleasedAction  =
					new KBMenuRReleasedAction(kbm);
				kbmRReleasedAction.setKBMenuRPressedAction(kbmRPressedAction);
				kbmRPressedAction.setKBMenuRReleasedAction(kbmRReleasedAction);
				jpanel.
					getInputMap().
					put(KeyStroke.getKeyStroke("R"), "kbmRPressedAction");
				jpanel
					.getActionMap().put("kbmRPressedAction", kbmRPressedAction);
				
				
				jpanel
					.getInputMap()
					.put(KeyStroke.getKeyStroke("released R"), "kbmRReleasedAction");
				jpanel
					.getActionMap().put("kbmRReleasedAction", kbmRReleasedAction);
				
				
				jpanel.requestFocusInWindow();
//				kbm.addKeyListener(new TestKBMenuKeyListener(kbm));
				
				
				jfrm.pack();
				jfrm.setVisible(true);
				
			}
		});
	}
}



class KBMenuRPressedAction extends AbstractAction {
	private KeyboardMenu kbm;
	private boolean rPressedAlready = false;
	private KBMenuRReleasedAction kbmRReleasedAction;
	
	KBMenuRPressedAction(KeyboardMenu kbm) {
		this.kbm = kbm;
	}
	
	public void actionPerformed(ActionEvent e) {
		if (!rPressedAlready) {
			KeyboardMenu.KMCard topOfStack = kbm.kmcStack.peek();
			//topOfStack.translate(-10, -10);
			kbm.pushKMCard(topOfStack.getSubCard(KeyboardMenu.KMKey.R));
			rPressedAlready = true;
			kbmRReleasedAction.resetRReleasedAlready();
		}
	}
	
	public void resetRPressedAlready() {
		rPressedAlready = false;
	}
	
	public void setKBMenuRReleasedAction(KBMenuRReleasedAction kbmRRAction) {
		kbmRReleasedAction = kbmRRAction;
	}
}

class KBMenuRReleasedAction extends AbstractAction {
	private KeyboardMenu kbm;
	private boolean rReleasedAlready = false;
	private KBMenuRPressedAction kbmRPressedAction;
	
	public KBMenuRReleasedAction(KeyboardMenu kbm) {
		this.kbm = kbm;
	}
	
	public void actionPerformed(ActionEvent e) {
		if (!rReleasedAlready & 
				kbm.kmcStack.peek().equals(kbm.getKMCard("base_r"))) {
			kbm.popKMCard();
			//kbm.kmcStack.peek().translate(10, 10);
			rReleasedAlready = true;
			kbmRPressedAction.resetRPressedAlready();
		}
	}
	
	public void resetRReleasedAlready() {
		rReleasedAlready = false;
	}
	
	public void setKBMenuRPressedAction(KBMenuRPressedAction kbmRPAction) {
		kbmRPressedAction = kbmRPAction;
	}
}


class TestKBMenuKeyListener implements KeyListener {
	private KeyboardMenu kbm;
	private boolean rPressed;
	private boolean jPressed;
	
	TestKBMenuKeyListener(KeyboardMenu kbm) {
		super();
		this.kbm = kbm;
		rPressed = false;
		jPressed = false;
	}
	
	public void keyPressed(KeyEvent e) {
		System.out.println("keyPressed method called");
		
		KeyboardMenu.KMCard topOfStack = kbm.kmcStack.peek();
		//need to have a case statement for each key
		if (e.getKeyCode() == KeyEvent.VK_R & rPressed == false) {
			kbm.pushKMCard(topOfStack.getSubCard(KeyboardMenu.KMKey.R));
			rPressed = true;
		} else if (e.getKeyCode() == KeyEvent.VK_J & jPressed == false) {
			PPath jKey = topOfStack.getKey(KeyboardMenu.KMKey.J);
			jKey.setStrokePaint(new Color(255,0,0));
			jKey.setStroke(new BasicStroke(3.0f));
			jPressed = true;
		}
	}
	public void keyReleased(KeyEvent e) {
		System.out.println("keyReleased method called");
		
		if (e.getKeyCode() == KeyEvent.VK_R 
				& kbm.kmcStack.peek().equals(kbm.getKMCard("base_r"))) {
			kbm.popKMCard();
			rPressed = false;
		} else if (e.getKeyCode() == KeyEvent.VK_J & jPressed == true) {
			PPath jKey = kbm.getKMCard("base_r").getKey(KeyboardMenu.KMKey.J);
			jKey.setStrokePaint(new Color(0,0,0));
			jKey.setStroke(new BasicStroke(1.0f));
			jKey = kbm.getKMCard("base").getKey(KeyboardMenu.KMKey.J);
			jKey.setStrokePaint(new Color(0,0,0));
			jKey.setStroke(new BasicStroke(1.0f));
			jPressed = false;
		}
		
	}
	public void keyTyped(KeyEvent e) {
		
	}
}
